
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>


#define SHELLWIDTH      80

void Usage(const char * filename)
{
        printf("Usage: %s -a IP -p PORT -f FILE -i INTERFACE [-s SLEEPTIME] [-r] [-l DATA LENGTH] [-6]\r\n", filename);
}

void paint_progress(unsigned int sent, unsigned int length)
{
        int     i;
        int     dec;

        dec = sent * SHELLWIDTH / length;

        printf("\r[");
        for(i=0; i<dec; i++)
        {
                printf("#");
        }
        for(;i<SHELLWIDTH; i++)
        {
                printf(" ");
        }
        printf("](%d/%d)(%0.2f%%)            ", sent, length, (float)sent / length * 100.0f);
        fflush(stdout);

}


int msend(const char * eth, const char * ipaddr, short int port,  const char * filename, int datalength, int repeat, int msec, int ipv6)
{

        int                     sock;
        struct sockaddr_in      addr;
        struct sockaddr_in6     addr6;
        FILE    *               fp;
        char    *               buf;
        size_t                  nret;
        short int               tport;

        unsigned int            filelength;
        unsigned int            bytessent;

        if (ipv6)
            sock = socket(PF_INET6, SOCK_DGRAM, 0);
        else
            sock = socket(PF_INET, SOCK_DGRAM, 0);
        if(sock < 0)
        {
                perror("socket");
                return -1;
        }


        if (ipv6) {
            addr6.sin6_family = AF_INET6;
            inet_pton(AF_INET6, eth, &addr6.sin6_addr);
            tport = 10234;
            while (1) {
                addr6.sin6_port = htons(tport);
                printf("bind to port %d\n", tport);
                if (bind(sock, (struct sockaddr*)&addr6, sizeof(addr6)) < 0) {
                    perror("bind");
                    if (tport > 10240)
                        return -2;
                    tport ++;
                    continue;
                }
                break;
            }
        } else {
            addr.sin_family = AF_INET;
            //addr.sin_addr.s_addr = htonl(INADDR_ANY);
            addr.sin_addr.s_addr = inet_addr(eth);
            tport = 10234;

            while(1) {
                addr.sin_port = htons(tport);
                printf("bind to port %d\n", tport);
                if(bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
                    perror("bind");
                    if(tport > 10240) {
                        return -2;
                    }
                    tport ++;
                    continue;
                }
                break;
            }
        }
        printf("bind at %d\r\n", tport);


        buf = (char *)malloc(datalength);
        printf("port=%d\r\n", port);
        printf("addr=%s\r\n", ipaddr);

        if (ipv6) {
            addr6.sin6_family = AF_INET6;
            addr6.sin6_port  = htons(port);
            if (inet_pton(AF_INET6, ipaddr, &addr6.sin6_addr) <= 0) {
                perror("inet_pton");
                return -1;
            }
        } else {
            addr.sin_family = AF_INET;
            addr.sin_port = htons(port);
            //addr.sin_addr.s_addr = inet_addr(ipaddr);
            if(inet_pton(AF_INET, ipaddr, &addr.sin_addr) <= 0)
            {
                perror("inet_pton");
                return -1;
            }
        }

        fp = fopen(filename, "rb");
        if(fp == NULL)
        {
                perror("fopen");
                return -3;
        }

        fseek(fp, 0, SEEK_END);
        filelength = ftell(fp);
        bytessent = 0;

        printf("sending...\r\n");
        do
        {
                fseek(fp, 0, SEEK_SET);
                bytessent = 0;
                while( (nret = fread(buf, 1, datalength, fp)) > 0 )
                { 
                    int ret ;
                    if (ipv6) {
                        ret = sendto(sock, buf, nret, 0, (struct sockaddr *)&addr6, sizeof(addr6));
                    } else {
                        ret = sendto(sock, buf, nret, 0, (struct sockaddr *)&addr, sizeof(addr));
                    }

                    if(ret < 0) {
                        perror("sendto");
                        return -4;
                    }
                    usleep(msec * 1000);
                    bytessent += nret;
                    paint_progress(bytessent/datalength, filelength/datalength);
                }
                printf("\r\n Send complete.\r\n");
        }while(repeat);

        free (buf);
        close(sock);
        fclose(fp);

        return 0;
}


int main(int argc, char * argv[])
{

        int      opt;
        char     address[1024];
        char     filename[1024];
        char     eth[1024];
        int      port;
        int      argcount;
        int      datalength;
        int      repeat;
        int      msec;
        int      ipv6 = 0;

        memset(address, 0, 1024);
        memset(eth, 0, 1024);
        memset(filename, 0, 1024);
        port = 0;
        datalength = 1024;
        repeat = 0;
        argcount = 0;
        msec = 0;

        while( (opt = getopt(argc, argv, "i:a:p:f:rl:hs:6")) != -1 )
        {
                switch (opt)
                {
                case 'i':
                        strncpy(eth, optarg, 1024);
                        argcount ++;
                        break;
                case 'a':
                        strncpy(address, optarg, 1024);
                        argcount ++;
                        break;
                case 'p':
                        port = atoi(optarg);
                        if(port <= 0 || port >= 65535)
                        {
                                Usage(argv[0]);
                                return -1;
                        }
                        argcount ++;
                        break;
                case 'h':
                        Usage(argv[0]);
                        return 0;
                case 'r':
                        repeat = 1;
                        break;
                case 'f':
                        strncpy(filename, optarg, 1024);
                        argcount++;
                        break;
                case 'l':
                        datalength = atoi(optarg);
                        if(datalength <= 0)
                        {
                                datalength = 1024;
                        }
                        break;
                case 's':
                        msec = atoi(optarg);
                        if(msec < 0)
                        {
                                Usage(argv[0]);
                                return -1;
                        }
                        break;
                case '6':
                        ipv6 = 1;
                        break;
                default:
                        Usage(argv[0]);
                        return -1;
                }
        }

        if(argcount != 4)
        {
                Usage(argv[0]);
                return -1;
        }
        
        while( msend(eth, address, (short)port, filename, datalength, repeat, msec, ipv6) != 0 )
        {
                usleep(100000);
        }

        return 0;
}


